/*
   Copyright 2011 Brook Smith

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
package org.xempot.xform;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class SnippitParser implements Callable<SnippetComponent> {

	public static void main(String[] args) {
		try {
			System.out.println(parseSnippit("<hello>${/*;heading}</hello>", null));
		} catch (InstructionException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Break the snippit down into its subparts to be used by the
	 * document/snippit processor
	 * 
	 * @param snippit
	 * @return
	 * @throws Exception
	 */
	public static List<SnippetComponent> parseSnippit(String snippit, Transform transform) throws InstructionException {
		ExecutorService executorService = Executors.newCachedThreadPool();
		List<Future<SnippetComponent>> snippetFutures = new ArrayList<Future<SnippetComponent>>();

		int offset = 0;
		// basically we are looking for any number of ${.+}
		// first replace escaped \${ instances with some other thing
		if (snippit.contains("\\${")) {
			throw new InstructionException("escaping is not implement yet");
		}
		int start = 0;
		while ((start = snippit.indexOf("${", offset)) > 0) {
			snippetFutures.add(executorService.submit(new SnippitParser(offset, start, snippit,
					SnippitSubpartType.string, transform)));

			offset = snippit.indexOf("}", start) + 1;
			if (offset < 0) {
				// this might be more useful with location information
				throw new InstructionException("Unmatched instruction close (missing }) past " + start + ", " + snippit);
			}
			// TODO Thread this
			snippetFutures.add(executorService.submit(new SnippitParser(start, offset, snippit,
					SnippitSubpartType.instruction, transform)));
		}
		snippetFutures.add(executorService.submit(new SnippitParser(offset, snippit.length(), snippit,
				SnippitSubpartType.string, transform)));

		executorService.shutdown();

		// transfer the futures into the result - get is blocking so it will
		// wait for any incomplete
		List<SnippetComponent> snippetComponents = new ArrayList<SnippetComponent>();
		for (Future<SnippetComponent> future : snippetFutures) {
			try {
				snippetComponents.add(future.get());
			} catch (Exception e) {
				throw new InstructionException(e.toString(), e);
			}
		}

		return snippetComponents;
	}

	/*
	 * Internal Thread for extracting the data.
	 */
	final int start, end;
	final String snippit;
	final SnippitSubpartType snippitSubpartType;
	final Transform transform;

	private SnippitParser(int start, int end, String snippit, SnippitSubpartType snippitSubpartType, Transform transform) {
		this.start = start;
		this.end = end;
		this.snippit = snippit;
		this.snippitSubpartType = snippitSubpartType;
		this.transform = transform;
	}

	@Override
	public SnippetComponent call() throws InstructionException {
		if (snippitSubpartType == SnippitSubpartType.instruction) {
			return new SnippetComponent(SnippitSubpartType.instruction, InstructionParser.parseCommand(
					snippit.substring(start, end), transform));
		}
		return new SnippetComponent(SnippitSubpartType.string, snippit.substring(start, end));
	}

}
